﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace PlatformerCS
{
    class PerlinNoise
    {
        List<Vector2> points = new List<Vector2>();
        GameBoard game;
        Random rand;
        GraphicsDevice gr;

        private byte[] perm = new byte[512] { 151,160,137,91,90,15,
              131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
              190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
              88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
              77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
              102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
              135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
              5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
              223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
              129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
              251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
              49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
              138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
              151,160,137,91,90,15,
              131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
              190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
              88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
              77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
              102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
              135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
              5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
              223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
              129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
              251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
              49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
              138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
            };


        public PerlinNoise(GraphicsDevice gr, GameBoard game)
        {
            this.gr = gr;
            this.game = game;
            rand = new Random();
        }

        public void Generate()
        {
            points.Clear();

            for (int i = 0; i < GameBoard.WIDTH; i++)
            {
                Vector2 p = new Vector2(i, PerlinGet(i));
                points.Add(p);
            }

            for (int i = 0; i < GameBoard.WIDTH; i++)
            {
                points[i] = new Vector2(points[i].X * 5, ((points[i].Y + 1) * 100) + 400);
            }

        }

        public void Draw(SpriteBatch spirteBatch)
        {
            for (int i = 1; i < points.Count; i++)
                game.DrawLine(points[i - 1], points[i]);
        }

        /* Perlin noise methods */
        float PerlinRandom(int x)
        {
            x = (x << 13) ^ x;
            return (float)((1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0));    
        }

        float SmoothedNoise(int x)
        {
            return (PerlinRandom(x) / 2 + PerlinRandom(x - 1) / 4 + PerlinRandom(x + 1) / 4);
        }

        // Float or int x
        float PerlinCosInterpolate(float a, float b, float x)
        {
	        float ft = (float)(x * 3.1415927);
	        float f = (float)((1 - Math.Cos(ft)) * 0.5);

            return a * (1 - f) + b * f;
        }

        float PerlinInterpolatedNoise(float x)
        {
            int intX = (int)x;
            float fracX = x - intX;

            float v1 = SmoothedNoise((int)x);
            float v2 = SmoothedNoise((int)x + 1);

            return PerlinCosInterpolate(v1, v2, fracX);
        }

         float PerlinGet(float x)
         {
             int persistence = 2;
             int octaves = 2;

             float total = 0f;
             int p = persistence;
             int n = octaves - 1;

             for (int i = 0; i < octaves; i++)
             {
                int frequency = (int)Math.Pow(2, i);
                int amplitude = (int)Math.Pow(p, i);

                total = total + PerlinInterpolatedNoise(x * frequency) * amplitude;
             }

             return total;
        } 
    }
}
